#include <kSerial.h>
#include "logger_rte.h"
#include "plc_abi.h"
#include "boardbase.h"
#include "plc_rte.h"
#include "SerialFirmata.h"
#include "Holder.h"

#if defined( USE_RTC) || defined(USE_PCF8563)

#include "rte_rtc.h"

#endif
#ifdef USE_UDISK

#include "Udisk.h"

#endif

const char *hwModel = HWMODEL;

#ifdef USE_MCU_FLASH

#include "retain_stm32.h"
#include "Holder.h"

retain_stm32 stm32(APP_START, APP_START, FLASHEND - APP_START);
#endif
#ifdef USE_LFS

#include <retain_lfs.h>

retain_lfs dev_lfs;
#endif
#ifdef USE_PROGFLASH

#include "DevProgFlash.h"

DevProgFlash dev_prog_flash;
#endif
#ifdef windows_x86
#include "retain_file.h"
retain_file dev_file;
#endif
#ifdef MONITOR_SERIAL

#include "retain_lfs.h"

#endif
#ifdef USE_SD

#include <STM32SD.h>

#endif
#ifdef RTE_APP

int boardBase::modbus_read_serial(u16 start_addr, u16 word_count, u16 *data_words, kSerial *serial, u16 start,
                                  u16 len) {
#ifdef ARDUINO_ARCH_STM32
    if ((start_addr >= start) && ((start_addr + word_count) <= (start + len))) {
        const u8 *buf =serial->_serial.rx_buff;
        for (int i = start_addr - start; i < (start_addr - start + word_count); ++i) {
            if (128 == i) {
                //                *data_words = serial->rx_head;
            } else if (129 == i) {
                //                *data_words = serial->rx_tail;
            } else {
                *data_words = (u16) (buf[i * 2] << 8) + buf[i * 2 + 1];
            }
            ++data_words;
        }
        return 0;
    }

    if ((start_addr >= (start + len)) && ((start_addr + word_count) <= (start + len + len))) {

        const u8 *buf = serial->_serial.tx_buff;
        for (int i = (start_addr - start - len); i < (start_addr - start - len + word_count); ++i) {
            if (128 == i) {
                //                *data_words = serial->tx_head;
            } else if (129 == i) {
                //                *data_words = serial->tx_tail;
            } else {
                *data_words = (u16) (buf[i * 2] << 8) + buf[i * 2 + 1];
            }
            ++data_words;
        }
        return 0;
    }
#endif
    return -2;
}


#endif
#ifndef THIS_IS_BOOTLOADER

int boardBase::get_din(u16 ch, u8 *pVoid) const {
    auto val = pVoid;
    if (ch < IO_XI_NRS) {
        *val = rte_data.X[ch];
        return *val;
    }
    return 0;
}

int boardBase::get_dout(u16 ch, u8 *pVoid) const {
    u8 *val = pVoid;
    if (ch < IO_YO_NRS) {
        *val = rte_data.Y[ch];
        return *val;
    }
    return 0;
}

void boardBase::set_dout(u16 ch, u8 *pVoid) {
    auto val = pVoid;
    if (ch < IO_YO_NRS) {
        rte_data.Y[ch] = *val;
    }
}

void boardBase::set_output_fast(u8 l2, u8 inx, u8 l4, u8 l5, u8 val) {
    u8 l3 = inx - 1;
    if (l3 < IO_YO_NRS) {
        rte_data.Y[l3] = val;
        //        digitalWrite(yport[l3], val);
    }
}

u8 boardBase::get_input_fast(u8 l2, u8 inx, u8 l4, u8 l5) {
    u8 l3 = inx - 1;
    if (l3 < IO_XI_NRS) {
        return rte_data.X[l3];
    }
    return 0;
}

int boardBase::modbus_read_pin(u16 start_addr, u16 word_count, u16 *data_words, u16 start, u16 len) {
    if ((start_addr >= start) && ((start_addr + word_count) <= start + len)) {
        for (uint32_t i = start_addr - start; i < (start_addr - start + word_count); ++i) {
            *data_words = rte_data.X[i];
            ++data_words;
        }
        return 0;
    }
    return -2;
}

int boardBase::modbus_read_analog_value(u16 start_addr, u16 word_count, u16 *data_words, u16 start, u16 len) {
    if ((start_addr >= start) && ((start_addr + word_count) <= (start + len))) {
        for (uint32_t i = start_addr - start; i < (start_addr - start + word_count); ++i) {
            if ((ANALOGVALUE_LENGTH) <= i) {
                return -2;
            }
            *data_words = rte_data.XA[i];
            ++data_words;
        }
        return 0;
    }
    return -2;
}

int boardBase::modbus_write_pin(u16 start_addr, u16 word_count, u16 *data_words, u16 start, u16 len) {
    if ((start_addr >= start) && ((start_addr + word_count) <= (start + len))) {
        for (uint32_t i = start_addr - start; i < start_addr - start + word_count; ++i) {
            rte_data.X[i] = *data_words;
            ++data_words;
        }
        return 0;
    }
    return -2;
}

int boardBase::modbus_write_analog_value(u16 start_addr, u16 word_count, u16 *data_words, u16 start, u16 len) {
    if (start_addr >= start && (start_addr + word_count) <= start + len) {
        for (uint32_t i = start_addr - start; i < start_addr - start + word_count; ++i) {
            if (i < (ANALOGVALUE_LENGTH)) {
                rte_data.XA[i] = *data_words;
            }
            ++data_words;
        }
        return 0;
    }
    return -2;
}

int boardBase::modbus_read_di(u16 start_addr, u16 bit_count, u8 *data_words, u16 start, u16 len) const {
    u16 byte;
    if ((start_addr >= start) && ((start_addr + bit_count) <= (start + len))) {
        byte = 0;
        for (u16 coils_processed = 0; coils_processed < bit_count;) {
            data_words[byte] = 0;
            for (u8 bit = 0x01; ((bit & 0xFF) != 0) && (coils_processed < bit_count); bit <<= 1, ++coils_processed) {
                u8 tmp;
                if (get_din((u16) start_addr - start + coils_processed,
                            &tmp)) {
                    data_words[byte] |= bit;
                } /*   set bit */
                else {
                    data_words[byte] &= ~bit;
                } /* reset bit */
            }
            byte++;
        }
        return 0;
    }
    return -2;
}

int boardBase::modbus_read_do(u16 start_addr, u16 bit_count, u8 *data_words, u16 start, u16 len) const {
    u8 temp;
    u16 byte;
    if ((start_addr >= start) && ((start_addr + bit_count) <= (start + len))) {
        byte = 0;
        for (u16 coils_processed = 0; coils_processed < bit_count;) {
            data_words[byte] = 0;
            for (u8 bit = 0x01; (bit & 0xFF) && (coils_processed < bit_count); bit <<= 1, coils_processed++) {
                if (get_dout(start_addr - start + coils_processed, &temp)) {
                    data_words[byte] |= bit;
                } /*   set bit */
                else {
                    data_words[byte] &= (~bit);
                } /* reset bit */
            }
            byte++;
        }
        return 0;
    }
    return -2;
}

int boardBase::modbus_write_do(u16 start_addr, u16 bit_count, const u8 *data_words, u16 start, u16 len) {
    u8 temp;
    u16 byte;
    if ((start_addr >= start) && ((start_addr + bit_count) <= (start + len))) {
        byte = 0;
        for (u16 coils_processed = 0; coils_processed < bit_count;) {
            temp = data_words[byte];
            for (int bit = 0x01; (bit & 0xff) && (coils_processed < bit_count); bit <<= 1, coils_processed++) {
                u8 v = (temp & bit) ? 1 : 0;
                set_dout(start_addr - start + coils_processed, &v);
            }
            byte++;
        }
        return 0;
    }
    return -2;
}

#ifdef RTE_APP

int boardBase::modbus_cb_read(u16 start_addr, u16 word_count, u16 *data_words) {
    if (modbus_read_pin(start_addr, word_count, data_words, 63000, TOTAL_PINS) == 0) {
        return 0;
    }
    if (modbus_read_analog_value(start_addr, word_count, data_words, 63500, (ANALOGVALUE_LENGTH)) == 0) {
        return 0;
    }
#ifdef ARDUINO
    if ((start_addr >= MODBUS_SERAIL_START) &&
        ((start_addr + word_count) < (MODBUS_SERAIL_START + kSerial::Serials_nrs * 300))) {
        u8 port = (start_addr - MODBUS_SERAIL_START) / 300;
        if (0 ==
            modbus_read_serial(start_addr, word_count, data_words, kSerial::get_serial(port),
                               (MODBUS_SERAIL_START + (port * 300)),
                               130))
            return 0;
    }
#endif
    uint32_t id;
    union {
        uint32_t ui;
        uint8_t uc[4];
    } u;
    for (int adr = start_addr; adr < start_addr + word_count; adr++) {
        switch (adr) {
#ifdef USE_IP_MODIFY
            case 60000:
                u.ui = rte_config.lwip.ip;
                *data_words = u.uc[0];
                break;
            case 60001:
                u.ui = rte_config.lwip.ip;
                *data_words = u.uc[1];
                break;
            case 60002:
                u.ui = rte_config.lwip.ip;
                *data_words = u.uc[2];
                break;
            case 60003:
                u.ui = rte_config.lwip.ip;
                *data_words = u.uc[3];
                break;
            case 60004:
                u.ui = rte_config.lwip.mask;
                *data_words = u.uc[0];
                break;
            case 60005:
                u.ui = rte_config.lwip.mask;
                *data_words = u.uc[1];
                break;
            case 60006:
                u.ui = rte_config.lwip.mask;
                *data_words = u.uc[2];
                break;
            case 60007:
                u.ui = rte_config.lwip.mask;
                *data_words = u.uc[3];
                break;
            case 60008:
                u.ui = rte_config.lwip.gw;
                *data_words = u.uc[0];
                break;
            case 60009:
                u.ui = rte_config.lwip.gw;
                *data_words = u.uc[1];
                break;
            case 60010:
                u.ui = rte_config.lwip.gw;
                *data_words = u.uc[2];
                break;
            case 60011:
                u.ui = rte_config.lwip.gw;
                *data_words = u.uc[3];
                break;
#if LWIP_DNS
                case 60012:
                    u.ui = rte_config.lwip.dns;
                    *data_words = u.uc[0];
                    break;
                case 60013:
                    u.ui = rte_config.lwip.dns;
                    *data_words = u.uc[1];
                    break;
                case 60014:
                    u.ui = rte_config.lwip.dns;
                    *data_words = u.uc[2];
                    break;
                case 60015:
                    u.ui = rte_config.lwip.dns;
                    *data_words = u.uc[3];
                    break;
#else
            case 60012:
                *data_words = eth1.data.ipSet;
                break;
#endif
            case 60016:
                *data_words = eth1.data.ipSet;
                break;
#endif
#ifdef ARDUINO_ARCH_STM32
            case 60017:
                id = HAL_GetUIDw0();
                *data_words = (u16) (id >> 16);
                break;
            case 60018:
                id = HAL_GetUIDw0();
                *data_words = (u16) id;
                break;
            case 60019:
                id = HAL_GetUIDw1();
                *data_words = (u16) (id >> 16);
                break;
            case 60020:
                id = HAL_GetUIDw1();
                *data_words = (u16) id;
                break;
            case 60021:
                id = HAL_GetUIDw2();
                *data_words = (u16) (id >> 16);
                break;

            case 60022:
                id = HAL_GetUIDw2();
                *data_words = (u16) id;
                break;
#endif
            case 60023:
                *data_words = (u16) (Rtos::ticks() >> 16);
                break;
            case 60024:
                *data_words = (u16) Rtos::ticks();
                break;
            case 60025:
                *data_words = *(const u16 *) hwModel;
                break;
            case 60026:
                *data_words = *(const u16 *) &hwModel[2];
                break;
            case 60027:
                *data_words = *(const u16 *) &hwModel[4];
                break;
            case 60028:
                *data_words = *(const u16 *) &hwModel[6];
                break;
#ifdef ARDUINO
            case 60029:
                *data_words = rte.data.remain_mem;
                break;
            case 60030:
                *data_words = rte.data.toInit;
                break;
            case 60031:
                *data_words = rte.data.state == pb_state_EXEC_BOOT;
                break;
            case 60032:
                *data_words = os.OSCPUUsage();
                break;
#endif
            default:
                return -2;
                // break;
        }
        ++data_words;
    }
    return 0;
}

int boardBase::modbus_cb_write(u16 start_addr, u16 word_count, u16 *data_words) {
    if (modbus_write_pin(start_addr, word_count, data_words, 63000, TOTAL_PINS) == 0)
        return 0;
    if (modbus_write_analog_value(start_addr, word_count, data_words, 63500, (ANALOGVALUE_LENGTH)) == 0)
        return 0;
    union {
        uint32_t ui;
        uint8_t uc[4];
    } u;
    for (int adr = start_addr; adr < start_addr + word_count; adr++) {
        switch (adr) {
#ifdef USE_IP_MODIFY
            case 60000:
                u.ui = rte_config.lwip.ip;
                u.uc[0] = (u8) *data_words;
                rte_config.lwip.ip = u.ui;
                break;
            case 60001:
                u.ui = rte_config.lwip.ip;
                u.uc[1] = (u8) *data_words;
                rte_config.lwip.ip = u.ui;
                break;
            case 60002:
                u.ui = rte_config.lwip.ip;
                u.uc[2] = (u8) *data_words;
                rte_config.lwip.ip = u.ui;
                break;
            case 60003:
                u.ui = rte_config.lwip.ip;
                u.uc[3] = (u8) *data_words;
                rte_config.lwip.ip = u.ui;
                break;
            case 60004:
                u.ui = rte_config.lwip.mask;
                u.uc[0] = (u8) *data_words;
                rte_config.lwip.mask = u.ui;
                break;
            case 60005:
                u.ui = rte_config.lwip.mask;
                u.uc[1] = (u8) *data_words;
                rte_config.lwip.mask = u.ui;
                break;
            case 60006:
                u.ui = rte_config.lwip.mask;
                u.uc[2] = (u8) *data_words;
                rte_config.lwip.mask = u.ui;
                break;
            case 60007:
                u.ui = rte_config.lwip.mask;
                u.uc[3] = (u8) *data_words;
                rte_config.lwip.mask = u.ui;
                break;
            case 60008:
                u.ui = rte_config.lwip.gw;
                u.uc[0] = (u8) *data_words;
                rte_config.lwip.gw = u.ui;
                break;
            case 60009:
                u.ui = rte_config.lwip.gw;
                u.uc[1] = (u8) *data_words;
                rte_config.lwip.gw = u.ui;
                break;
            case 60010:
                u.ui = rte_config.lwip.gw;
                u.uc[2] = (u8) *data_words;
                rte_config.lwip.gw = u.ui;
                break;
            case 60011:
                u.ui = rte_config.lwip.gw;
                u.uc[3] = (u8) *data_words;
                rte_config.lwip.gw = u.ui;
                break;
#if LWIP_DNS
                case 60012:
                    u.ui = rte_config.lwip.dns;
                    u.uc[0] = (u8)*data_words;
                    rte_config.lwip.dns = u.ui;
                    break;
                case 60013:
                    u.ui = rte_config.lwip.dns;
                    u.uc[1] = (u8)*data_words;
                    rte_config.lwip.dns = u.ui;
                    break;
                case 60014:
                    u.ui = rte_config.lwip.dns;
                    u.uc[2] = (u8)*data_words;
                    rte_config.lwip.dns = u.ui;
                    break;
                case 60015:
                    u.ui = rte_config.lwip.dns;
                    u.uc[3] = (u8)*data_words;
                    rte_config.lwip.dns = u.ui;
                    break;
#else
            case 60012:
                eth1.data.ipSet = (u8) *data_words;
                break;

#endif
            case 60016:
                eth1.data.ipSet = (u8) *data_words;
                break;
#endif
            case 60030:
                rte.data.toInit = (u8) *data_words;
                break;
            case 60031:
                if (*data_words == 1)
                    rte.event(pb_event_REQUEST_RESTART, 1);
                break;
            default:
                return -2;
        }
        ++data_words;
    }
    return word_count;
}

int boardBase::modbus_cb_read_bits(u16 start_addr, u16 word_count, u8 *data_words) {
    if (modbus_read_di(start_addr, word_count, data_words, 60000, IO_XI_NRS) == 0)
        return 0;
    if (modbus_read_do(start_addr, word_count, data_words, MODBUS_Y_START, IO_YO_NRS) == 0)
        return 0;
    return -2;
}

int boardBase::modbus_cb_write_bits(u16 start_addr, u16 word_count, u8 *data_words) {
    if (modbus_write_do(start_addr, word_count, data_words, MODBUS_Y_START, IO_YO_NRS) == 0)
        return 0;
    return -2;
}

int boardBase::modbus_cb_write_bit(u16 start_addr, u16 bit_count, u8 data_words) {
    u16 byte;
    if ((start_addr >= MODBUS_Y_START) && ((start_addr + bit_count) <= (MODBUS_Y_START + IO_YO_NRS))) {
        byte = 0;
        for (u16 coils_processed = 0; coils_processed < bit_count;) {
            for (int bit = 0x01; (bit & 0xff) && (coils_processed < bit_count); bit <<= 1, coils_processed++) {
                u8 v = 0xff == data_words ? 1 : 0;
                set_dout(start_addr - MODBUS_Y_START + coils_processed, &v);
            }
            byte++;
        }
        return 0;
    }
    return -2;
}

#endif
#ifdef USE_BOOTLOADER

int boardBase::flash_bootloader_from_lfs()
{
#if USE_LFS
    auto buf = (char *)malloc(FLASH_BLOCK_SIZE);
    FlashFile bin{};
    if (bin.open_rdonly("bootloader.bin") == 0)
    {
        uint32_t len = bin.size("bootloader.bin");
        erasePage(0, BOOT_BIN_PAGE_NRS);
        for (int i = 0; i < len;)
        {
            bin.read(buf, FLASH_BLOCK_SIZE);
            writeFlash(0x08000000, buf, FLASH_BLOCK_SIZE);
            i += FLASH_BLOCK_SIZE;
        }
    }
    else
    {
        return pb_event_FILE_NOT_EXIST;
    }
    free(buf);
#endif
    return 0;
}

#endif

int boardBase::set_output(plc_loc_tbl_t tbl) {
    auto val = (byte *) tbl->v_buf;
    auto inx = (int) tbl->a_data[1];
    auto l2 = (int) tbl->a_data[2];
    auto l3 = (int) tbl->a_data[3];
    return set_output(tbl->a_data[0], inx, l2, l3, val, tbl->v_size);
}

int boardBase::set_output(u8 l0, u8 inx, u8 l2, u8 l3, byte *val, u8 v_size) {
    switch (l0) {
        case REGION_DIGITAL:
            return rte_data.X[inx] = *(u8 *) val;
        case REGION_XI:
            return 0;
        case REGION_YO:
            set_dout((u16) inx - 1, (u8 *) val);
            return *(char *) val;
        case REGION_XA:
            //            set_ain(tbl);
            return 0;
        case REGION_YA:
        case REGION_YAA:
            set_aout(inx - 1, (u16 *) val);
            return 0;
        case REGION_SYS:
            switch (inx) {
#if defined( USE_RTC) || defined(USE_PCF8563)
                case 1:
                    rte_data.wyear = *(uint16_t *) val;
                    break;
                case 2:
                    rte_data.wmonth = *(uint8_t *) val;
                    break;
                case 3:
                    rte_data.wday = *(uint8_t *) val;
                    break;
                case 4:
                    rte_data.whour = *(uint8_t *) val;
                    break;
                case 5:
                    rte_data.wminute = *(uint8_t *) val;
                    break;
                case 6:
                    rte_data.wsecond = *(uint8_t *) val;
                    break;
                case 7:
                    rte_data.wweek = *(uint8_t *) val;
                    break;
                case 8:
                    if (*(uint8_t *) val)
                        rte_data.rtc_write = 1;
                    else
                        rte_data.rtc_write = 0;
                    break;
#endif
                case 9:
                    rte.data.wdg_en = *(uint8_t *) val;
                    break;
                case 10:
                    rte_config.wdg_time = *(uint16_t *) val;
                    break;
                case 11:
                    if (*(uint8_t *) val)
                        rte.event(pb_event_REQUEST_RESTART, 1);
                    break;
                case 14:
                    rte.data.state = (pb_state) *(uint8_t *) val;
                    break;
                case 15:
                    rte_data.alarm = *(uint8_t *) val;
                    break;
                case 17:
                    rte.data.toInit = *(uint8_t *) val;
                    if (rte.data.toInit) {
                        initReset();
                        rte.data.toInit = 0;
                    }
                    break;
                default:
                    break;
            }
            break;
        case REGION_16:
            return rte_data.XA[inx] = *(s16 *) val;
        case REGION_32:
            return rte_data.xa32[inx - 1] = *(s32 *) val;
        case REGION_INFO:
            return setInfoValue(inx, v_size, val);
        case REGION_CONFIG:
            return holder.setHoldValue(inx, v_size, val);
        case REGION_HOLDER:
            return holder.setHoldValue(inx, v_size, val);
        default:
            break;
    }
    return 0;
}

int boardBase::get_input(plc_loc_tbl_t tbl) {
    auto val = (byte *) tbl->v_buf;
    auto inx = (int) tbl->a_data[1];
    auto ch = (int) tbl->a_data[0];
    return get_input(ch, inx, tbl->a_data[2], tbl->v_size, val);
}

int boardBase::get_input(int ch, int inx, int l2, int size, void *val) {
    union {
        uint32_t ui;
        uint8_t uc[4];
    } u;
    switch (ch) {
        case REGION_DIGITAL:
            *(uint8_t *) val = rte_data.X[inx - 1];
            return *(uint8_t *) val;
        case REGION_XI:
            *(char *) val = (char) get_din((u16) inx - 1, (u8 *) val);
            return *(char *) val;
        case REGION_YO:
            *(char *) val = (char) get_dout((u16) inx - 1, (u8 *) val);
            return *(char *) val;

        case REGION_XA:
            *(uint16_t *) val = (u16) get_ain(inx - 1, val);
            return *(uint16_t *) val;
        case REGION_YA:
        case REGION_YAA:
            if ((inx > 0) && (inx <= IO_YA_NRS)) {
                *(uint16_t *) val = rte_data.YA[inx - 1];
            }
            return *(uint16_t *) val;
        case REGION_SYS:
            switch (inx) {
#if defined( USE_RTC) || defined(USE_PCF8563)

                case 1:
                    *(uint16_t *) val = rtc.data.year;
                    return 2;
                case 2:
                    *(uint8_t *) val = rtc.data.month;
                    return 1;
                case 3:
                    *(uint8_t *) val = rtc.data.day;
                    return 1;
                case 4:
                    *(uint8_t *) val = rtc.data.hour;
                    return 1;
                case 5:
                    *(uint8_t *) val = rtc.data.minute;
                    return 1;
                case 6:
                    *(uint8_t *) val = rtc.data.second;
                    return 1;
                case 7:
                    *(uint8_t *) val = rtc.data.week;
                    return 1;
                case 8:
                    *(uint8_t *) val = rte_data.rtc_write;
                    return 1;
#endif
                case 9:
                    *(uint8_t *) val = rte.data.wdg_en;
                    return 1;
                case 10:
                    *(uint16_t *) val = rte_config.wdg_time;
                    return 2;
                case 11:
                    *(uint8_t *) val = rte.data.state == pb_state_EXEC_BOOT;
                    return 1;
                case 12:
                    *(uint32_t *) val = Rtos::ticks() / 0xffff;
                    return 4;
                case 13:
                    *(uint32_t *) val = Rtos::ticks() % 0xffff;
                    return 4;
                case 14:
                    *(uint8_t *) val = rte.data.state;
                    return 1;
                case 15:
                    *(uint8_t *) val = rte_data.alarm;
                    return 1;
                case 16:
                    *(uint32_t *) val = rte.data.remain_mem;
                    return 4;
                case 17:
                    *(uint8_t *) val = rte.data.toInit;
                    return 1;
                case 18:
                    // *(uint8_t *)val = enUSBHost;
                    return 0;
#ifdef USE_IP_MODIFY
                case 19:
                    u.ui = rte_config.lwip.ip;
                    *(uint8_t *) val = u.uc[0];
                    return 0;
                case 20:
                    u.ui = rte_config.lwip.ip;
                    *(uint8_t *) val = u.uc[1];
                    return 0;
                case 21:
                    u.ui = rte_config.lwip.ip;
                    *(uint8_t *) val = u.uc[2];
                    return 0;
                case 22:
                    u.ui = rte_config.lwip.ip;
                    *(uint8_t *) val = u.uc[3];
                    return 0;
                case 23:
                    u.ui = rte_config.lwip.mask;
                    *(uint8_t *) val = u.uc[0];
                    return 0;
                case 24:
                    u.ui = rte_config.lwip.mask;
                    *(uint8_t *) val = u.uc[1];
                    return 0;
                case 25:
                    u.ui = rte_config.lwip.mask;
                    *(uint8_t *) val = u.uc[2];
                    return 0;
                case 26:
                    u.ui = rte_config.lwip.mask;
                    *(uint8_t *) val = u.uc[3];
                    return 0;
                case 27:
                    u.ui = rte_config.lwip.gw;
                    *(uint8_t *) val = u.uc[0];
                    return 0;
                case 28:
                    u.ui = rte_config.lwip.gw;
                    *(uint8_t *) val = u.uc[1];
                    return 0;
                case 29:
                    u.ui = rte_config.lwip.gw;
                    *(uint8_t *) val = u.uc[2];
                    return 0;
                case 30:
                    u.ui = rte_config.lwip.gw;
                    *(uint8_t *) val = u.uc[3];
                    return 0;
                case 31:
                    *(uint8_t *) val = eth1.data.ipSet;
                    return 0;
#endif
                case 32:
                    *(uint8_t *) val = rte_data.prompt;
                    break;
                default:
                    logger.error("io get:unHandle %d.%d\n", ch, inx);
                    return -1;
            }
            break;
        case REGION_16:
            *(short *) val = (short) rte_data.XA[inx];
            return 2;
        case REGION_32:
            memcpy(val, ((u8 *) (&rte_data.xa32)) + inx, 4);
            return 4;
        case REGION_INFO:
            return getInfoValue(inx, val, size);
        case REGION_CONFIG:
            return holder.getHoldValue(inx, val, size);
        case REGION_HOLDER:
            return holder.getHoldValue(inx, val, size);
        default:
            logger.error("io get: unHandle %d.%d\n", ch, inx);
            return -2;
    }
    return -1;
}

void boardBase::flashClear() {
    logger.info("flashClear\n");
#ifdef USE_DATA_FLASH
#if defined(RTE_APP) || defined(PLC)
    rte.app_supsund();
#endif
    rte.event(pb_event_req_FLASH_FORMAT, 1);
    dataFlash->chipErase();
    Rtos::Delay(10);
    rte.event(pb_event_REQUEST_RESTART, 1);
#endif
}

int boardBase::get_ain(u8 ch, void *v_buf) const {
    auto val = (u16 *) v_buf;
    if (ch >= 0 && ch < IO_XA_NRS) {
        *val = rte_data.XA[ch];
        return *val;
    }
    return 0;
}

int boardBase::get_aout(int ch, u16 *pVoid) const {
    auto val = pVoid;
    if (ch >= 0 && ch < IO_YA_NRS) {
        *val = rte_data.YA[IO_XA_NRS + ch];
        return *val;
    }
    return 0;
}

void boardBase::output(uint32_t tick) {
    if (enOut)
        board_output(tick);
}

void boardBase::set_aout(int ch, const u16 *pVoid) const {
    auto val = pVoid;
    if (ch >= 0 && ch < IO_XA_NRS + IO_YA_NRS) {
        rte_data.YA[IO_XA_NRS + ch] = (s16) *val;
    }
}

int boardBase::get_dbg_jmpr_get() {
    return 0;
}

int boardBase::get_rst_jmpr_get() {
    return 0;
}

#ifdef USE_COPY_RTE_LFS

int boardBase::usb_copy_to_lfs(const char *name)
{
    int res;
    auto buf = (char *)malloc(1024);
    int len;
    if (udisk.open_file_rdonly(name) == 0)
    {
        rte.event(PLC_STATUS::USB_COPYING);
        udisk.stat(name);
        int s = udisk.fsize;
        FlashFile b;
        if (0 <= b.open_write(name))
        {

            while (s > 0)
            {
                len = udisk.read(buf, 1024);
                if (!len)
                    break;
                logger.debug("A: len=%d s=%d\n", len, s);
                if (b.write(buf, len) < 0)
                {
                    res = -1;
                    break;
                }
                long_time_cb();
                s -= len;
            }
            b.close();
            if (0 == s)
            {
                logger.debug("copy ok");
                rte.event(PLC_STATUS::USB_COPIED);
                res = 0;
            }
        }
        udisk.close();
    }
    else
    {
        rte.event(PLC_STATUS::USB_NO_FILE);
        res = -2;
    }
    free(buf);
    return res;
}

#endif
#ifdef USE_LFS

int lastbuildPLC_md5(u8 *txbuf) {
    volatile int buflen = 0;
    if (FlashFile::stat("lastbuildPLC.md5") > 0) {
        FlashFile md{};
        md.open_rdonly("lastbuildPLC.md5");
        buflen = md.read((char *) txbuf, 32);
        txbuf[buflen] = 0;
        // uri = txbuf;
        md.close();
    } else {
        txbuf[0] = '0';
        buflen = 1;
    }
    return buflen;
}

#endif

u8 boardBase::get_din(u16 ch) const {
    if (ch < IO_XI_NRS) {
        return rte_data.X[ch];
    }
    return 0;
}

#undef write

int boardBase::capability_query(mFirmata *, nStream *FirmataStream) {
#ifdef FIRMATA_FAST
    for (int pin = 0; pin < IO_YO_NRS + IO_XI_NRS + IO_XA_NRS + IO_YA_NRS; ++pin)
    {
        if (pin < IO_XI_NRS)
            FirmataStream->write((u8)PIN_MODE_INPUT);
        else if ((pin >= IO_XI_NRS) && (pin < IO_YO_NRS + IO_XI_NRS))
        {
            FirmataStream->write(PIN_MODE_OUTPUT);
        }
        else if ((pin >= (IO_YO_NRS + IO_XI_NRS)) && (pin < (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS)))
        {
            FirmataStream->write(PIN_MODE_ANALOG);
        }
        else if ((pin >= (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS)) &&
                 (pin < (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS + IO_YA_NRS)))
        {
            FirmataStream->write(PIN_MODE_PWM);
        }
        FirmataStream->write(127);
    }
#endif
    return 0;
}

int boardBase::analog_mapping_query(mFirmata *fm, nStream *FirmataStream) {
#ifdef FIRMATA_FAST
    for (int pin = 0; pin < (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS + IO_YA_NRS); ++pin)
    {
        if ((pin < (IO_XI_NRS + IO_XA_NRS)) && (pin >= IO_XI_NRS))
        {
            FirmataStream->write(pin - IO_XI_NRS);
        }
        else
        {
            FirmataStream->write(127);
        }
    }
#endif
    return 0;
}

#ifndef FIRMATA_DISABLE_REPORT

int boardBase::readAnalogValue(mFirmata *fm, nStream *FirmataStream, uint64_t analogInputsToReport, byte length) {
    for (uint32_t pin = 0; (ANALOGVALUE_LENGTH) > pin; ++pin) {
        if (bitRead(analogInputsToReport, pin) != 0x0) {
            int val = rte_data.XA[pin];
            fm->sendAnalog(FirmataStream, (byte) pin, val);
            FirmataStream->flush();
        }
    }
    return 0;
}

#endif

void boardBase::outputPort(byte i, bool b) {
    if (i >= IO_XI_NRS) {
        rte_data.Y[i] = b;
    }
}

int boardBase::setPinState(mFirmata *fm, nStream *, u8 pin, int value) {
    if (pin < IO_XI_NRS) {
        //
    } else if ((pin >= IO_XI_NRS) && (pin < (IO_YO_NRS + IO_XI_NRS))) {
        rte_data.Y[pin] = value;
    } else if ((pin >= (IO_YO_NRS + IO_XI_NRS)) && (pin < (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS))) {
        //
    } else if ((pin >= (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS)) &&
               (pin < (IO_YO_NRS + IO_XI_NRS + IO_XA_NRS + IO_YA_NRS))) {
        rte_data.YA[pin] = value;
    } else {
        //
    }
    //    fm->setPinState(pin, value);
    return 0;
}

#endif
#ifdef __PLATFORMIO_BUILD_DEBUG__
#include <cassert>
int boardBase::dev_test(uint32_t tick)
{
#ifdef USE_DATA_FLASH
    assert(dataFlash);
#endif
    //    kSerial *s = kSerial::get_serial(1);
    //    s->begin(9600);
    //    s->write((const u8 *)"Serial2", 7);
    return 0;
}
#endif

int boardBase::begin(uint32_t tick) {
#ifndef THIS_IS_BOOTLOADER
    led_err(0);
    led_run(0);
    led_pwr(1);
#endif
    rte_data.X = x;
    rte_data.X_count = IO_XI_NRS;
    rte_data.Y = y;
    rte_data.Y_count = IO_YO_NRS;
    rte_data.XA = xa;
    rte_data.XA_count = IO_XA_NRS;
    rte_data.YA = ya;
    rte_data.YA_count = IO_YA_NRS;
    rte_data.cpu = cpu;
    rte_data.has_cpu = 1;
    rte_data.firmata = firmata;
    rte_data.has_firmata = 1;
    rte_config.XI_PORT = xi_sel;
    rte_config.XI_PORT_count = IO_XI_NRS;
    rte_config.YO_PORT = yo_sel;
    rte_config.YO_PORT_count = IO_YO_NRS;
    rte_config.XA_PORT = xa_sel;
    rte_config.XA_PORT_count = IO_XA_NRS;
    rte_config.YA_PORT = ya_sel;
    rte_config.YA_PORT_count = IO_YA_NRS;
    bvref.begin(0, 0xfff);
    bvbat.begin(0, 0xfff);
    btemp.begin(0, 0xfff);
    return 0;
}

#ifdef USE_LFS

int boardBase::read_file(const char *file, char *buf, int len) {
    FlashFile myFile{};
    int res = 0;
    {
        //        logger.info("read lfs: %s ", file);
        res = myFile.open_rdonly(file);
        if (res < 0) {
            logger.error("Open %s failed 0x%x", file, res);
            return pb_event_NO_DEVICE;
        }
        int sz = myFile.size(file);
        //        logger.info("size=%d", sz);
        res = myFile.read(buf, len);
        if (res <= 0) {
            logger.error("read %d bytes failed 0x%x", len, sz);
        }
    }
    myFile.close();
    return res;
}

#ifndef LFS_READONLY

int boardBase::write_file(const char *file, const char *buf, int len) {
    FlashFile myFile{};
    int res = 0;
    {
        //        logger.info("%s appendrte_data.size=%d", file, len);
        res = myFile.open_append(file);
        if (res < 0) {
            logger.error("Open %s failed 0x%x", file, res);
        } else {
            int sz = myFile.size(file);
            //            logger.info("size=%d", sz);
            if (myFile.fseek(sz, LFS_SEEK_SET) >= 0) {
                res = myFile.write(buf, len);
                if (res != len) {
                    logger.error("Write %d bytes failed 0x%x", len, sz);
                }
            } else {
                logger.error("seek fail.");
            }
        }
    }
    myFile.close();
    return res;
}

int boardBase::remove_file(const char *file) {
    return FlashFile::unlink(file);
}

#endif
#endif

void boardBase::led(uint32_t tick) {
    if (rte.data.state < (u8) pb_state_Stopped) {
        led_run(tick & 0x80);
    } else if (rte.data.state >= pb_state_WAIT_USB_OFF) {
        led_run((tick & 0x200) ? 1 : 0);
        led_err(tick & 0x200);
        led_pwr((tick & 0x200) ? 0 : 1);
    } else if (rte.data.state > (u8) pb_state_Started) {
        led_run(tick & 0x40);
    } else {
        //
    }
    if (rte.data.last_error != 0) {
        led_err(err_led_tick++ & 0x40);
        if (err_led_tick > 10000) {
            err_led_tick = 0;
            rte.data.last_error = 0;
        }
    }
}


#ifdef FORWARD_NRS

void boardBase::forwardMessage(char port, uint8_t c)
{
    if (port < FORWARD_NRS)
    {
        if (forwardStream[port] != nullptr)
        {
            forwardStream[port]->flag |= FLAG_FORWARD;
            forwardStream[port]->write(c);
        }
    }
}

#endif

#ifdef USE_LFS
#ifndef PLC

int boardBase::lfs2ram(const char *name, u8 *start) {
    //    logger.debug("lfs2ram enter.");
    auto *bin = new FlashFile();
    if (bin->open_rdonly((const char *) name) == 0) {
        int s = bin->size(name);
        if (s > 0) {
            logger.debug("dup %s to 0x%x, size=0x%x\n", name, start, s);
            int len = bin->read((char *) start, s);
            return 0;
        }
    }
    logger.error("lfs2ram Failed.");
    return -1;
}

#endif
#endif

kSerial *SerialFirmata::getPortFromId(byte portId) {
    return kSerial::get_serial(portId);
}

extern "C" [[noreturn]] void _error_handler(char *s, int num) {
    /* USER CODE BEGIN Error_Handler */
    /* User can add his own implementation to report the HAL error return state */
    logger.error("error in %s , %d Line", s, num);
    while (true) {
        //
    }
    /* USER CODE END Error_Handler */
}
extern "C" void _Infinite_Loop() {
#ifndef THIS_IS_BOOTLOADER
    core_debug("Unkonwn IRQ Handler!!!");
#endif
}
extern "C" void Infinite_Loop() {
#ifndef THIS_IS_BOOTLOADER
    core_debug("Unkonwn IRQ Handler!!!");
#endif
}
mem_block *mem_block::mems[] = {
#if defined(USE_MCU_FLASH)
        &stm32,
#else
        nullptr,
#endif
#ifdef USE_LFS
        &dev_lfs,
#else
#ifdef windows_x86
        &dev_file,
#else
        nullptr,
#endif
#endif
#ifdef USE_PROGFLASH
        &dev_prog_flash,
#else
        nullptr,
#endif
};

int mem_block::Write(byte *, int) {
    logger.error("mem_block::Write not support");
    return 0;
}

uint32_t mem_block::length() {
    return 0;
}

#ifdef USE_COPY_USB_DATAFLASH

int boardBase::usb_to_dataFlash(const char *name, uint32_t flash_start_addr, uint32_t header_size)
{
    uint32_t adr;
    adr = flash_start_addr + header_size;
    rte.event(USB_COPYING);

    char *buf = (char *)malloc(1024);
    int len;
    if (udisk.open_file_rdonly(name) == 0)
    {
        rte.event(USB_COPYING);
        udisk.stat(name);
        int s = udisk.fsize;
        dataFlash->blockErase64K(adr);
        while (s > 0)
        {
            //            udisk.loop(Rtos::ticks());
            len = udisk.read(buf, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            dataFlash->writeBytes(adr, (const uint8_t *)buf, len);
            adr += len;
            s -= len;
        }
        // setAppProtect();
        udisk.close();
        core_debug("flash %s -> %p ok", name, flash_start_addr);
        free(buf);
        rte.event(USB_COPIED);
    }
    else
    {
        rte.event(USB_NO_FILE);
    }
    return 0;
}

#endif

void boardBase::led_pwr(int i) {
    //
}

int boardBase::run(uint32_t tick) {
#ifdef USE_CPU_ADC
    get_cpu_info();
#endif
#ifndef THIS_IS_BOOTLOADER
    if (rte.data.state >= pb_state_Stopped && rte.data.state <= pb_state_STATE_POWER_FAULT) {
        Poll(tick);
    }
#endif
#ifdef PLC
    output(tick);
#endif

#ifdef ARDUINO
    static uint32_t lastTick = 0;
    if (rte.data.state == pb_state_EXEC_BOOT) {
        if (last_state != pb_state_EXEC_BOOT) {
            last_state = pb_state_EXEC_BOOT;
            lastTick = tick + 200;
            logger.warning("pb_state_EXEC_BOOT");
        }
        if (tick > lastTick) {
            reset();
        }
    }
#ifndef THIS_IS_BOOTLOADER
    if (rte.data.toInit) {
        logger.info("reset to init");
        initReset();
        rte.data.toInit = 0;
    }
#ifdef USE_IAP
    if (rte.data.state == pb_state_EXEC_IAP)
    {
        if (last_state != pb_state_EXEC_IAP)
        {
            last_state = pb_state_EXEC_IAP;
            lastTick = tick + 200;
            inlineCtrl.data->iap = pb_state_EXEC_IAP;
        }
        logger.info("BOOT_WAIT_IAP");
        if (tick > lastTick)
            reset();
    }
#endif
#endif
#endif
#ifdef USE_LFS_UPDATE_BOOTLOADER
    if (flashFile->exist("bootloader.bin") == 0)
    {
        logger.info("found bootloader.bin");
        if (flash_bootloader_from_lfs() == 0)
        {
            logger.info("bootloader.bin update ok");
            flashFile->remove("bootloader.bin");
        }
    }
#endif
    return 0;
}

#ifdef USE_SD

int boardBase::write_file_to_sd(const char *file, const char *buf, int len)
{
    File myFile;
    int res = 0;
    if (SD.begin())
    {
        if (SDClass::exists(file))
        {
            logger.info("%s exists.appendrte_data.size=%d", file, len);
        }
        else
        {
            logger.info("%s doesn't exist. create. size=%d", file, len);
        }
        myFile = SDClass::open(file, FILE_WRITE);
        if (!myFile)
        {
            logger.error("Open %s failed 0x%x", file, myFile._res);
            res = -1;
        }
        int sz = myFile.size();
        logger.info("size=%d", sz);
        if (myFile.seek(sz))
        {
            int sz = myFile.write(buf, len);
            if (sz != len)
            {
                logger.error("Write %d bytes failed 0x%x", len, sz);
            }
        }
        else
        {
            logger.error("seek fail.");
            res = -2;
        }
        // myFile.flush();
    }
    // else{
    //     logger.error("SD open fail. skip write_file %s",file);
    // }
    myFile.close();
    SD.end();
    return res;
}

#ifdef USE_LFS

int boardBase::copy_file_lfs2sd(const char *src, const char *dst)
{
    FlashFile srcFile{};
    File destFile;
    int sz = -1;
    int res = 0;
    if (SD.begin())
    {
        res = srcFile.open_rdonly(src);
        if (res < 0)
        {
            logger.error("Open %s failed 0x%x", src, res);
            res = pb_event_FILE_NOT_EXIST;
        }
        else
        {
            sz = srcFile.size(src);

            if (SDClass::exists(dst))
            {
                logger.info("%s exists.appendrte_data.", dst);
            }
            else
            {
                logger.info("%s doesn't exist. create. size=%d", dst, sz);
            }
            destFile = SDClass::open(dst, FILE_WRITE);
            if (!destFile)
            {
                logger.error("Open %s failed 0x%x", dst, destFile._res);
                res = pb_event_NO_DEVICE;
            }
            else
            {
                //        logger.info("size=%d", sz);
                u8 *buf = (u8 *)malloc(4096);
                for (int i = 0; i < sz; i += 4096)
                {
                    int len = srcFile.read((char *)buf, 4096);
                    if (len > 0)
                    {
                        destFile.write(buf, len);
                    }
                }
                free(buf);
            }
        }
        srcFile.close();
        destFile.close();
    }
    SD.end();
    return sz;
}

int boardBase::cp_sd2lfs(const char *src, const char *dst)
{
    File srcFile{};
    FlashFile destFile{};
    int sz = -1;
    int res = 0;
    if (SD.begin())
    {
        if (SDClass::exists(src))
        {
            srcFile = SDClass::open(src, FILE_READ);
            if (FR_OK != srcFile.getErrorstate())
            {
                logger.error("Open %s failed.", src);
                res = pb_event_FILE_NOT_EXIST;
            }
            else
            {
                sz = (int)srcFile.size();

                res = destFile.open_write(dst);
                if (res < 0)
                {
                    logger.error("Open %s failed 0x%x", dst, res);
                    res = pb_event_NO_DEVICE;
                }
                else
                {
                    auto *buf = (u8 *)malloc(4096);
                    for (int i = 0; i < sz; i += 4096)
                    {
                        int len = srcFile.read((char *)buf, 4096);
                        if (len > 0)
                        {
                            destFile.write((const char *)buf, len);
                        }
                    }
                    free(buf);
                    logger.info("sd2lfs copy %s to %s ok.", src, dst);
                }
                destFile.close();
            }
            srcFile.close();
        }
        else
        {
            logger.info("%s doesn't exist", src);
        }
    }
    else
    {
        logger.error("SD open fail. skip copy file %s", src);
    }
    SD.end();
    return res;
}

#ifdef USE_FREERTOS

void boardBase::copy_file_lfs2sd_thread(void *arg)
{
    auto *t = (copy_file_t *)arg;
    TaskHandle_t thd = t->thread_handle;
    logger.info("copy file in thread %p %s(LFS) to %s (SD)", thd, t->src, t->dst);
    int res = copy_file_lfs2sd(t->src, t->dst);
    if (0 <= res)
    {
        logger.info("copy file %s(LFS) to %s (SD) ok ,size=%d", t->src, t->dst, res);
    }
    else
    {
        logger.error("copy file %s(LFS) to %s (SD)  fail %d", t->src, t->dst, res);
    }
    free(t);
    rte_data.file_copy = 0;
    vTaskDelete(thd);
}

#endif

int boardBase::sd_read_file(const char *file, char *buf, int len)
{
    int sz = -1;
    int res = 0;
    File destFile;
    if (SD.begin())
    {
        if (SDClass::exists(file))
        {
            destFile = SDClass::open(file, FILE_READ);
            if (!destFile)
            {
                logger.error("Open %s failed 0x%x", file, destFile._res);
                return pb_event_NO_DEVICE;
            }
            for (int i = 0; i < len; i += 4096)
            {
                int len = destFile.read(buf + i, 4096);
                if (0 >= len)
                {
                    break;
                }
            }
        }
        else
        {
            logger.error("%s doesn't exist.", file);
        }
        destFile.close();
        SD.end();
    }
    else
    {
        logger.error("SD open fail. skip read_file %s", file);
    }
    return sz;
}

#endif
#endif
#if defined(USE_FILE)

void boardBase::write_file_thread(void *p)
{
    auto t = (write_file_t *)p;
    os.Delay(200); // 等待结构体填充线程句柄
    TaskHandle_t thd = t->thread_handle;
    //    logger.debug("in write_file_thread %p %s %d", thd, t->file, t->len);
    os.Delay(1000);
    int res = write_file(t->file, t->data, t->len);
    if (0 <= res)
    {
        logger.info("write file %s ok ,size=%d", t->file, res);
    }
    else
    {
        logger.error("write file %s fail %d", t->file, res);
    }
    free(t);
    rte_data.file_write = 0;
    vTaskDelete(thd);
}

#endif
#ifndef THIS_IS_BOOTLOADER

void boardBase::initReset() {
#ifdef USE_KVDB
    kvdb.remove("retain");
#endif
}

int boardBase::diag(uint32_t tick) {

    return 0;
}

#endif
#ifdef ARDUINO

void BKP_SRAM_unInit();

int boardBase::reset() {

//    logger.info("exec reset");
#ifdef ARDUINO_ARCH_STM32
    HAL_Delay(250);
    HAL_NVIC_SystemReset();
#endif
    //    logger.error("reboot failed!");
    return 0;
}

#endif
#ifdef USE_LFS

uint32_t boardBase::lfs_file_crc32(const char *name) {
    unsigned int crc = 0;
    FlashFile lf;

    char *buf = (char *) malloc(1024);
    int len;
    if (lf.open_rdonly(name) == 0) {
        int s = lf.size(name);
        while (s > 0) {
            len = lf.read(buf, 1024);
            if (!len)
                break;
            crc_16((u8 *) buf, len, crc);
            s -= len;
        }
        // setAppProtect();
        lf.close();
        free(buf);
    } else {
    }
    return crc;
}

#endif

int boardBase::setInfoValue(u8 inx, u8 size, byte *val) {
    return 0;
}

int boardBase::getInfoValue(int inx, void *pVoid, int size) {
    return 0;
}

#ifdef USE_FLASH_LFS_MV

int boardBase::lfs_to_progFlash(const char *name, uint32_t flash_start_addr, uint32_t header_size)
{
    uint32_t adr;
    FlashFile lf;
    adr = flash_start_addr + header_size;
    //    rte.event(pb_event_USB_COPYING_BEGIN, true);

    char *buf = (char *)malloc(1024);
    int len;
    if (lf.open_rdonly(name) == 0)
    {
        rte.event(pb_event_USB_COPYING_BEGIN, true);
        int s = lf.size(name);
        progFlash.blockErase64K(adr);
        while (s > 0)
        {
            len = lf.read(buf, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            progFlash.writeBytes(adr, (const uint8_t *)buf, len);
            adr += len;
            s -= len;
        }
        // setAppProtect();
        lf.close();
        core_debug("copy ok");
        free(buf);
        rte.event(pb_event_USB_COPYING_BEGIN, false);
    }
    else
    {
        rte.event(pb_event_USB_NO_FILE_event, true);
    }
    return 0;
}

#endif
#ifdef USE_LFS_TO_FLASH

int boardBase::lfs_to_Flash(const char *name, uint32_t flash_start_addr, uint32_t block_start, uint32_t block_nrs) {
    uint32_t adr;
    FlashFile lf;
    adr = flash_start_addr;
    rte.event(pb_event_USB_COPYING_BEGIN, true);

    char *buf = (char *) malloc(1024);
    int len;
    if (lf.open_rdonly(name) == 0) {
        rte.event(pb_event_USB_COPYING_BEGIN, true);
        int s = lf.size(name);
        erasePage(block_start, block_nrs);
        while (s > 0) {
            len = lf.read(buf, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            writeFlash(adr, (const char *) buf, len);
            adr += len;
            s -= len;
        }
        // setAppProtect();
        lf.close();
        core_debug("copy ok");
        free(buf);
    } else {
        rte.event(pb_event_USB_NO_FILE_event, true);
    }
    rte.event(pb_event_USB_COPYING_BEGIN, false);
    return 0;
}

#endif
#ifdef USE_USB_TO_MCU
char buf1[1024];

int boardBase::copy_usb_flash(const char *name, uint32_t start, uint32_t blk_start, uint32_t blk_nrs) {

    rte.event(pb_event_USB_COPYING_BEGIN, true);

    int len;
    udisk.stat(name);
    int s = udisk.fsize;
    if (udisk.open_file_rdonly(name) == 0) {
        rte.event(pb_event_USB_COPYING_BEGIN, true);
        erasePage(blk_start, blk_nrs);
        while (s > 0) {
            len = udisk.read((char *) buf1, 1024);
            if (!len)
                break;
            core_debug("A: len=%d s=%d\n", len, s);
            writeFlash(start, (char *) buf1, len);
            s -= len;
            start += len;
        }
        // setAppProtect();
        udisk.close();
        core_debug("flash ok");
    } else {
        rte.event(pb_event_USB_NO_FILE_event, true);
        //        core_debug("no file.");
    }
    rte.event(pb_event_USB_COPYING_BEGIN, false);
    return 0;
}

#endif

